package com.handpay.nocardpay.baselibrary.qualificationinfo.comm;

import android.content.ContentUris;
import android.content.Context;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.provider.DocumentsContract;
import android.provider.MediaStore;
import android.text.TextUtils;
import android.util.Log;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.BinaryBitmap;
import com.google.zxing.DecodeHintType;
import com.google.zxing.LuminanceSource;
import com.google.zxing.MultiFormatReader;
import com.google.zxing.PlanarYUVLuminanceSource;
import com.google.zxing.Result;
import com.google.zxing.common.HybridBinarizer;
import com.google.zxing.qrcode.QRCodeReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Hashtable;

/**
 * Created on 2017/11/1.<br/>
 * 二维码图片内容读取辅助。
 * @author fplei
 */
public class OkQrCodeReader {
    private static OkQrCodeReader qrCodeReader=null;
    /**
     * 解析选择选择图片返回的uri
     */
    public static class MetaUriUtil{
        private final static String DOCUMENT_AUTHORITY_URI="com.android.externalstorage.documents";
        private final static String DOWNLOAD_DOCUMENT_AUTHORITY_URI="com.android.providers.downloads.documents";
        private final static String MEDIA_DOCUMENT_AUTHORITY_URI="com.android.providers.media.documents";
        private final static String PHOTOS_CONTENT_AUTHORITY_URI="com.google.android.apps.photos.content";
        private final static String PRIMARY="primary";
        private final static String TYPE_IMAGE="image";
        private final static String TYPE_VIDEO="video";
        private final static String TYPE_AUDIO="audio";
        private final static String URI_CONTENT_SCHEME="content";
        private final static String URI_FILE_SCHEME="file";
        public static String getUriPath(Context context, Uri uri){
            if(uri == null) {
                return null;
            }
            if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && DocumentsContract.isDocumentUri(context, uri)) {
                if(DOCUMENT_AUTHORITY_URI.equals(uri.getAuthority())) {
                    final String docId = DocumentsContract.getDocumentId(uri);
                    final String[] split = docId.split(":");
                    final String type = split[0];
                    if(PRIMARY.equalsIgnoreCase(type)) {
                        return Environment.getExternalStorageDirectory() + "/" + split[1];
                    }
                } else if(DOWNLOAD_DOCUMENT_AUTHORITY_URI.equals(uri.getAuthority())) {
                    final String id = DocumentsContract.getDocumentId(uri);
                    final Uri contentUri = ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));
                    return getDataColumn(context, contentUri, null, null);
                } else if(MEDIA_DOCUMENT_AUTHORITY_URI.equals(uri.getAuthority())) {
                    final String docId = DocumentsContract.getDocumentId(uri);
                    final String[] split = docId.split(":");
                    final String type = split[0];
                    Uri contentUri = null;
                    if(TYPE_IMAGE.equals(type)) {
                        contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
                    } else if(TYPE_VIDEO.equals(type)) {
                        contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
                    } else if(TYPE_AUDIO.equals(type)) {
                        contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
                    }
                    final String selection = "_id=?";
                    final String[] selectionArgs = new String[] {split[1]};
                    return getDataColumn(context, contentUri, selection, selectionArgs);
                }
            } else if(URI_CONTENT_SCHEME.equalsIgnoreCase(uri.getScheme())) {
                if(PHOTOS_CONTENT_AUTHORITY_URI.equals(uri.getAuthority())) {
                    return uri.getLastPathSegment();
                }
                return getDataColumn(context, uri, null, null);
            } else if(URI_FILE_SCHEME.equalsIgnoreCase(uri.getScheme())) {
                return uri.getPath();
            }
            return null;
        }

        private static String getDataColumn(Context context, Uri uri, String selection, String[] selectionArgs) {
            Cursor cursor = null;
            final String column = "_data";
            final String[] projection = {column};
            try {
                cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, null);
                if(cursor != null && cursor.moveToFirst()) {
                    final int columnIndex = cursor.getColumnIndexOrThrow(column);
                    return cursor.getString(columnIndex);
                }
            } finally {
                if(cursor != null){ cursor.close();}
            }
            return null;
        }
    }

    public static class ImageUtils{
        private static ImageUtils imageUtils=null;
        public static ImageUtils with(){
            if(imageUtils==null){
                synchronized (ImageUtils.class){
                    if(imageUtils==null){
                        imageUtils=new ImageUtils();
                    }
                }
            }
            return imageUtils;
        }
        public Bitmap drawableToBitmap(Drawable drawable) {
            // 取 drawable 的长宽
            int w = drawable.getIntrinsicWidth();
            int h = drawable.getIntrinsicHeight();
            Log.i("result","w:"+w+",h:"+h);
            // 取 drawable 的颜色格式
            Bitmap.Config config = Bitmap.Config.ARGB_8888;
            // 建立对应 bitmap
            Bitmap bitmap = Bitmap.createBitmap(w, h, config);
            // 建立对应 bitmap 的画布
            Canvas canvas = new Canvas(bitmap);
            // 白色底色   应对透明图
            Paint paint = new Paint();
            paint.setColor(Color.WHITE);
            canvas.drawRect(0, 0, w, h, paint);
            drawable.setBounds(0, 0, w, h);
            // 把 drawable 内容画到画布中
            drawable.draw(canvas);
            return bitmap;
        }
        public byte[] getYUV420sp(int inputWidth, int inputHeight,Bitmap scaled) {
            int[] argb = new int[inputWidth * inputHeight];
            scaled.getPixels(argb, 0, inputWidth, 0, 0, inputWidth, inputHeight);
            byte[] yuv = new byte[inputWidth * inputHeight * 3 / 2];
            encodeYUV420SP(yuv, argb, inputWidth, inputHeight);
            scaled.recycle();
            return yuv;
        }
        private void encodeYUV420SP(byte[] yuv420sp, int[] argb, int width,int height) {
            // 帧图片的像素大小
            final int frameSize = width * height;
            // ---YUV数据---
            int Y, U, V;
            // Y的index从0开始
            int yIndex = 0;
            // UV的index从frameSize开始
            int uvIndex = frameSize;
            // ---颜色数据---
            int R, G, B;
            //
            int argbIndex = 0;
            //
            // ---循环所有像素点，RGB转YUV---
            for (int j = 0; j < height; j++) {
                for (int i = 0; i < width; i++) {
                    R = (argb[argbIndex] & 0xff0000) >> 16;
                    G = (argb[argbIndex] & 0xff00) >> 8;
                    B = (argb[argbIndex] & 0xff);
                    argbIndex++;
                    // well known RGB to YUV algorithm
                    Y = ((66 * R + 129 * G + 25 * B + 128) >> 8) + 16;
                    U = ((-38 * R - 74 * G + 112 * B + 128) >> 8) + 128;
                    V = ((112 * R - 94 * G - 18 * B + 128) >> 8) + 128;
                    Y = Math.max(0, Math.min(Y, 255));
                    U = Math.max(0, Math.min(U, 255));
                    V = Math.max(0, Math.min(V, 255));
                    yuv420sp[yIndex++] = (byte) Y;
                    if ((j % 2 == 0) && (i % 2 == 0)) {
                        if(yuv420sp.length!=uvIndex){
                            yuv420sp[uvIndex++] = (byte) V;
                            yuv420sp[uvIndex++] = (byte) U;
                        }
                    }
                }
            }
        }
        public byte[] rgb2YUV(Bitmap bitmap) {
            int width = bitmap.getWidth();
            int height = bitmap.getHeight();
            int[] pixels = new int[width * height];
            bitmap.getPixels(pixels, 0, width, 0, 0, width, height);
            int len = width * height;
            byte[] yuv = new byte[len * 3 / 2];
            int y, u, v;
            for (int i = 0; i < height; i++) {
                for (int j = 0; j < width; j++) {
                    int rgb = pixels[i * width + j] & 0x00FFFFFF;
                    int r = rgb & 0xFF;
                    int g = (rgb >> 8) & 0xFF;
                    int b = (rgb >> 16) & 0xFF;
                    y = ((66 * r + 129 * g + 25 * b + 128) >> 8) + 16;
                    u = ((-38 * r - 74 * g + 112 * b + 128) >> 8) + 128;
                    v = ((112 * r - 94 * g - 18 * b + 128) >> 8) + 128;
                    y = y < 16 ? 16 : (y > 255 ? 255 : y);
                    u = u < 0 ? 0 : (u > 255 ? 255 : u);
                    v = v < 0 ? 0 : (v > 255 ? 255 : v);
                    yuv[i * width + j] = (byte) y;
//                yuv[len + (i >> 1) * width + (j & ~1) + 0] = (byte) u;
//                yuv[len + (i >> 1) * width + (j & ~1) + 1] = (byte) v;
                }
            }
            return yuv;
        }
        /**
         * 根据给定的宽度和高度动态计算图片压缩比率
         *
         * @param options Bitmap配置文件
         * @param reqWidth 需要压缩到的宽度
         * @param reqHeight 需要压缩到的高度
         * @return 压缩比
         */
        public int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
            // Raw height and width of image
            final int height = options.outHeight;
            final int width = options.outWidth;
            int inSampleSize = 1;
            if (height > reqHeight || width > reqWidth) {
                final int halfHeight = height / 2;
                final int halfWidth = width / 2;
                // Calculate the largest inSampleSize value that is a power of 2 and keeps both
                // height and width larger than the requested height and width.
                while ((halfHeight / inSampleSize) > reqHeight && (halfWidth / inSampleSize) > reqWidth) {
                    inSampleSize *= 2;
                }
            }

            return inSampleSize;
        }

        /**
         * 将图片根据压缩比压缩成固定宽高的Bitmap，实际解析的图片大小可能和#reqWidth、#reqHeight不一样。
         * @param imgPath 图片地址
         * @param reqWidth 需要压缩到的宽度
         * @param reqHeight 需要压缩到的高度
         * @return Bitmap
         */
        public Bitmap decodeSampledBitmapFromFile(String imgPath, int reqWidth, int reqHeight) {
            // First decode with inJustDecodeBounds=true to check dimensions
            final BitmapFactory.Options options = new BitmapFactory.Options();
            options.inJustDecodeBounds = true;
            BitmapFactory.decodeFile(imgPath, options);
            // Calculate inSampleSize
            options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
            // Decode bitmap with inSampleSize set
            options.inJustDecodeBounds = false;
            return BitmapFactory.decodeFile(imgPath, options);
        }
        public void saveBitmap2File(Bitmap mbitmap, String path){
            Bitmap mtemp=mbitmap;
            File picfile=new File(path);
            if (picfile.exists()) {
                picfile.delete();
            }
            try {
                FileOutputStream out = new FileOutputStream(picfile);
                mtemp.compress(Bitmap.CompressFormat.PNG, 99, out);
                out.flush();
                out.close();
            } catch (FileNotFoundException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }

    /**
     * 返回一个实例
     * @return
     */
    public static OkQrCodeReader with(){
        if(qrCodeReader==null){
            synchronized (OkQrCodeReader.class){
                if(qrCodeReader==null){
                    qrCodeReader=new OkQrCodeReader();
                }
            }
        }
        return qrCodeReader;
    }

    /**
     * 识别图片二维码
     * @param context
     * @param codeFileUri
     * @return
     */
    public String readQrCodeContent(Context context, Uri codeFileUri){
        String codePath= MetaUriUtil.getUriPath(context,codeFileUri);
        if(TextUtils.isEmpty(codePath)){
            return "";
        }
        Bitmap scanBitmap= ImageUtils.with().decodeSampledBitmapFromFile(codePath,540,854);
        String resultString=null;
        try{
            resultString=readFromSingleQrCode(context,scanBitmap);
            if(TextUtils.isEmpty(resultString)){
                resultString=readFromMultiFormatQrCode(scanBitmap);
            }
            Log.i("result","resultString="+resultString);
            return resultString;
        }catch (Exception e){
            Log.i("result","readFromSingleQrCode Error:"+e.toString());
            try {
                resultString = readFromMultiFormatQrCode(scanBitmap);
            }catch (Exception e1){
                Log.i("result","readFromMultiFormatQrCode Error:"+e1.toString());
            }
            Log.i("result","resultString="+resultString);
            return resultString;
        }
    }

    /**
     * 使用专门的QRCodeReader类进行解析（大部分可以解析出来数据）
     * @param context
     * @param bitmap
     * @return
     */
    private String readFromSingleQrCode(Context context, Bitmap bitmap){
        Bitmap tempBitmap=bitmap;
        Drawable drawable=new BitmapDrawable(context.getResources(),tempBitmap);
        String readString = null;
        Bitmap bmp = ImageUtils.with().drawableToBitmap(drawable);
//        ImageUtils.with().saveBitmap2File(bmp, Environment.getExternalStorageDirectory().getAbsolutePath()+ File.separator+"splite.png");
        Log.i("result",bmp.getWidth()+","+bmp.getHeight());
        byte[] data = ImageUtils.with().getYUV420sp(bmp.getWidth(), bmp.getHeight(), bmp);
        // 处理
        try {
            Hashtable<DecodeHintType, Object> hints = new Hashtable<DecodeHintType, Object>();
            hints.put(DecodeHintType.CHARACTER_SET, "utf-8");
            hints.put(DecodeHintType.TRY_HARDER, Boolean.TRUE);
            hints.put(DecodeHintType.POSSIBLE_FORMATS, BarcodeFormat.QR_CODE);
            PlanarYUVLuminanceSource source = new PlanarYUVLuminanceSource(data,
                    bmp.getWidth(),
                    bmp.getHeight(),
                    0, 0,
                    bmp.getWidth(),
                    bmp.getHeight(),
                    false);
            BinaryBitmap bitmap1 = new BinaryBitmap(new HybridBinarizer(source));
            QRCodeReader reader2= new QRCodeReader();
            Result result = reader2.decode(bitmap1, hints);
            readString = result.getText();
        } catch (Exception e) {
            e.printStackTrace();
        }
        if(bmp != null && !bmp.isRecycled()){
            bmp.recycle();
            bmp = null;
        }
        return readString;
    }

    /**
     * 使用MultiFormatReader读取（该方式读取速度稍慢，解析数据更复杂）
     * @param bitmap
     * @return
     * @throws Exception
     */
    private String readFromMultiFormatQrCode(Bitmap bitmap)throws Exception {
        Bitmap scanBitmap=bitmap;
        Hashtable<DecodeHintType, String> hints = new Hashtable<DecodeHintType, String>();
        // 设置二维码内容的编码
        hints.put(DecodeHintType.CHARACTER_SET, "utf-8");
        LuminanceSource source1 = new PlanarYUVLuminanceSource(
                ImageUtils.with().rgb2YUV(scanBitmap), scanBitmap.getWidth(),
                scanBitmap.getHeight(), 0, 0, scanBitmap.getWidth(),
                scanBitmap.getHeight(),true);
        BinaryBitmap binaryBitmap = new BinaryBitmap(new HybridBinarizer(source1));
        MultiFormatReader reader1 = new MultiFormatReader();
        Result result1 = reader1.decode(binaryBitmap);
        String content = result1.getText();
        if(scanBitmap != null && !scanBitmap.isRecycled()){
            scanBitmap.recycle();
            scanBitmap = null;
        }
        return content;
    }
}
